package wechatpay

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"github.com/pkg/errors"
	"github.com/wechatpay-apiv3/wechatpay-go/core"
	"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
	"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
	"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
	"github.com/wechatpay-apiv3/wechatpay-go/services/payments/native"
	"io/ioutil"
	"log"
	"net/http"
	"pay/config"
	"time"
	"unsafe"
)

type NativePrePay struct {
	Description string `json:"description"`
	OutTradeNo  string `json:"outTradeNo"`
	NotifyUrl   string `json:"notifyUrl"`
	Total       int64  `json:"total"`
	TimeExpire  string `json:"timeExpire"`
	ReturnUrl   string `json:"return_url"`
}

// 预支付
func (nativePrePay *NativePrePay) Prepay() (string, error) {
	svc := native.NativeApiService{Client: MyClient.WechatClient}

	loc, _ := time.LoadLocation("Local")
	date, err := time.ParseInLocation("2006-01-02 15:04:05", nativePrePay.TimeExpire, loc)
	if err != nil {
		return "", errors.New("时间格式错误")
	}

	callbackUrl := nativePrePay.NotifyUrl + "/wechat"
	target := []byte(callbackUrl)
	toString := base64.StdEncoding.EncodeToString(target)
	resp, result, err := svc.Prepay(MyClient.Ctx,
		native.PrepayRequest{
			Attach:        core.String(toString),
			Appid:         core.String(MyClient.AppId),
			Mchid:         core.String(MyClient.MchID),
			Description:   core.String(nativePrePay.Description),
			OutTradeNo:    core.String(nativePrePay.OutTradeNo),
			TimeExpire:    core.Time(date),
			NotifyUrl:     core.String(config.Config.Wechat.CallbackUrl),
			SupportFapiao: core.Bool(false),
			Amount: &native.Amount{
				Currency: core.String("CNY"),
				Total:    core.Int64(nativePrePay.Total),
			},
		},
	)
	// 处理错误
	if err != nil {
		// 处理错误
		log.Println("call Prepay err:%s", err)
		return "", err
	} else {
		// 处理返回结果
		log.Println("status=%d resp=%s", result.Response.StatusCode, resp)
		return *resp.CodeUrl, nil
	}
}

type Close struct {
	OutTradeNo string `json:"outTradeNo"`
}

// 退款
func (close Close) NativeCloseOrder() (bool, error) {
	svc := native.NativeApiService{Client: MyClient.WechatClient}
	result, err := svc.CloseOrder(MyClient.Ctx,
		native.CloseOrderRequest{
			OutTradeNo: core.String(close.OutTradeNo),
			Mchid:      core.String(MyClient.MchID),
		},
	)
	if err != nil {
		// 处理错误
		log.Printf("call CloseOrder err:%s", err)
		return false, err
	} else {
		// 处理返回结果
		log.Printf("status=%d", result.Response.StatusCode)
		return true, nil
	}
}

func FindByOutTradeNo(outTradeNo string) (interface{}, error) {
	svc := native.NativeApiService{Client: MyClient.WechatClient}
	resp, result, err := svc.QueryOrderByOutTradeNo(MyClient.Ctx,
		native.QueryOrderByOutTradeNoRequest{
			OutTradeNo: core.String(outTradeNo),
			Mchid:      core.String(MyClient.MchID),
		},
	)
	if err != nil {
		// 处理错误
		log.Printf("call QueryOrderByOutTradeNo err:%s", err)
		return "", err
	} else {
		// 处理返回结果
		log.Printf("status=%d resp=%s", result.Response.StatusCode, resp)
		return resp, nil
	}
}

type CallbackContent struct {
	Attach        string    `json:"attach"`
	OutTradeNo    string    `json:"out_trade_no"`
	TransactionId string    `json:"transaction_id"`
	TradeType     string    `json:"trade_type"`
	TradeState    string    `json:"trade_state"`
	SuccessTime   time.Time `json:"success_time"`
	Payer         struct {
		Openid string `json:"openid"`
	}
	Amount struct {
		Total    int64  `json:"total"`
		Currency string `json:"currency"`
	}
}

func Callback(request *http.Request) (*payments.Transaction, error) {
	transaction := new(payments.Transaction)
	ctx := context.Background()
	// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
	err := downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, MyClient.MchPrivateKey, config.Config.Wechat.MchCertificateSerialNumber, config.Config.Wechat.MchId, config.Config.Wechat.MchAPIv3Key)
	// 2. 获取商户号对应的微信支付平台证书访问器
	if err != nil {
		return transaction, err
	}
	certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(config.Config.Wechat.MchId)
	// 3. 使用证书访问器初始化 `notify.Handler`
	handler := notify.NewNotifyHandler(config.Config.Wechat.MchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))

	notifyReq, err := handler.ParseNotifyRequest(context.Background(), request, transaction)
	// 如果验签未通过，或者解密失败
	if err != nil {
		fmt.Println(err)
		return transaction, err
	}
	// 处理通知内容
	var callbackdata CallbackContent
	err = json.Unmarshal([]byte(notifyReq.Resource.Plaintext), &callbackdata)

	res, err := base64.StdEncoding.DecodeString(callbackdata.Attach)
	if err != nil {
		panic(err)
	}
	callbackUrl := string(res)
	reader := bytes.NewReader([]byte(notifyReq.Resource.Plaintext))
	request1, err := http.NewRequest("POST", callbackUrl, reader)
	log.Println("回调日志", callbackUrl)
	if err != nil {
		fmt.Println(err.Error())
		return transaction, err
	}
	request1.Header.Set("Content-Type", "application/json;charset=UTF-8")
	client := http.Client{}
	resp, err := client.Do(request1)
	if err != nil {
		fmt.Println(err.Error())
		return transaction, err
	}
	respBytes, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err.Error())
		return transaction, err
	}
	//byte数组直接转成string，优化内存
	str := (*string)(unsafe.Pointer(&respBytes))
	fmt.Println(*str)

	if err != nil {
		fmt.Println(err)
		return transaction, err
	}
	return transaction, nil
}
